package com.ai.bitcoin.jpa.service;

import com.ai.bitcoin.exception.BusinessException;
import com.ai.bitcoin.jpa.dao.*;
import com.ai.bitcoin.jpa.entity.*;
import com.ai.bitcoin.vo.BccPageVo;
import com.ai.bitcoin.vo.PricePointVo;
import com.ai.bitcoin.vo.TradePointVO;
import com.google.common.collect.Sets;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.text.ParseException;
import java.util.*;

@Service
public class BccPriceInfoService {
    @Autowired
    @Getter
    BccPriceInfoDao dao;

    @Autowired
    @Getter
    BccPriceInfoDetailDao priceInfoDetailDao;

    @Autowired
    @Getter
    BccPriceEstimateDao bccPriceEstimateDao;

    @Autowired
    @Getter
    BccBasicInfoDao bccBasicInfoDao;

    @Autowired
    @Getter
    BccTradingPointDao bccTradingPointDao;

    public Page<BccPriceInfo> findByBatchNo(String batchNo, Pageable pageable) {
        return dao.findByBatchNo(batchNo, pageable);
    }

    @Transactional
    public void deleteNoPublish() {
        dao.deleteNoPublish();
    }

    public List<BccPriceInfo> findByBccIdAndBatchNo(String bccId, String batchNo) {
        return dao.findByBccIdAndBatchNo(bccId, batchNo);
    }

    public List<BccPriceInfo> findByBccIdAndRecordState(String bccId, String recordState) {
        return dao.findByBccIdAndRecordState(bccId, recordState);
    }

    public void update2Publish(String unPublishBatch) {
        dao.deleteOldPublish(unPublishBatch);
        dao.update2Publish(unPublishBatch);
    }

    public BccPriceInfo findOneByBccIdBetween(String bccId, Date fr, Date to) {
        return dao.findOneByBccIdBetween(bccId, fr, to);
    }

    public void delete2DayBefore() {
        dao.delete2DayBefore();
    }

    public Page<BccPriceInfo> findByBccIdAndPriceTimestampGreaterThan(String bccId, Date time, Pageable pageable) {
        return dao.findByBccIdAndPriceTimestampGreaterThan(bccId, time, pageable);
    }

    public Page<BccPriceInfo> findByBccId(String bccId, Pageable pageable) {
        return dao.findByBccId(bccId, pageable);
    }

    public List<BccPriceInfo> findByBccIdAndBatchNoAndRecordState(String bccId, String batchNo, String recordState) {
        return dao.findByBccIdAndBatchNoAndRecordState(bccId, batchNo, recordState);
    }

    public List<BccPriceInfo> findOneByBccIdAndRecordStateOrderByCreateTimeDesc(String bccId, String recordState) {
        return dao.findOneByBccIdAndRecordStateOrderByCreateTimeDesc(bccId, recordState);
    }

    public void deleteByBatchNo(String batchNo) {
        dao.deleteByBatchNo(batchNo);
    }

    public List<BccPriceInfo> findByBatchNo(String batchNo) {
        return dao.findByBatchNo(batchNo);
    }

    /**
     * 查询币价格信息
     *
     * @param bccId     币种
     * @param type      类型
     * @param isKeyLine 是否K线
     * @return 价格列表
     * @throws BusinessException 业务异常
     */
    public List<BccPriceInfoDetail> findPriceDetailByType(String bccId, String type, boolean isKeyLine) throws BusinessException {
        Date dateBegin = getDateBeginByType(type, isKeyLine);
        long minuteStep = 1;
        if (isKeyLine) {
            minuteStep = getMinuteStepByType(type);
        }
        return priceInfoDetailDao.queryLine(bccId, dateBegin, minuteStep);
    }

    private long getMinuteStepByType(String type) throws BusinessException {
        long minuteStep = 1;
        switch (type) {
            case "1m":
                break;
            case "5m":
                minuteStep = 5;
                break;
            case "10m":
                minuteStep = 10;
                break;
            case "30m":
                minuteStep = 30;
                break;
            case "1h":
                minuteStep = 60;
                break;
            case "1d":
                minuteStep = 24 * 60;
                break;
            case "7d":
                minuteStep = 7 * 24 * 60;
                break;
            case "1M":
            case "1y":
                minuteStep = 24 * 60;
                break;
            default:
                throw new BusinessException("不支持的类型:" + type);
        }
        return minuteStep;
    }

    //1m 5m 10m 30m 1h 1d 7d 1M 1y
    private Date getDateBeginByType(String type, boolean isKeyLine) throws BusinessException {
        Date dateBegin;
        Date todayBegin;
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        todayBegin = calendar.getTime();
        switch (type) {
            case "1m":
                dateBegin = todayBegin;
                break;
            case "5m":
            case "10m":
            case "30m":
            case "1h":
                dateBegin = DateUtils.addDays(todayBegin, -3);
                break;
            case "1d":
                dateBegin = DateUtils.addMonths(todayBegin, -1);
                break;
            case "7d":
                dateBegin = DateUtils.addMonths(todayBegin, -6);
                break;
            case "1M":
                dateBegin = DateUtils.addYears(todayBegin, -2);
                break;
            case "1y":
                dateBegin = DateUtils.addYears(todayBegin, -5);
                break;
            default:
                throw new BusinessException("不支持的类型:" + type);
        }
        return dateBegin;
    }

    /**
     * 取得预测价格数据
     *
     * @param bccId 预测币
     * @return 预测价格数据
     */
    public Set<PricePointVo> getPriceEstimate(String bccId, BccPageVo bccPageVo) {
        Set<PricePointVo> pointVoSet = Sets.newLinkedHashSet();
        List<BccPriceEstimate> estimateList = bccPriceEstimateDao.findPriceEstimateByBccId(bccId);
        for (BccPriceEstimate estimate : estimateList) {
            if (StringUtils.isEmpty(bccPageVo.getRecommend().getRiseChancePercent())) {
                //最近的预计上涨概率
                bccPageVo.getRecommend().setRiseChancePercent(
                        BigDecimal.valueOf(NumberUtils.toDouble(estimate.getProb(), 0) * 100).setScale(2, BigDecimal.ROUND_HALF_UP) + "%"
                );
            }
            try {
                pointVoSet.add(new PricePointVo(DateUtils.parseDate(estimate.getDs(), "yyyy-MM-dd HH:mm:ss"), estimate.getYhat()));
            } catch (ParseException ignored) {
            }
        }
        return pointVoSet;
    }


    /**
     * 取得预测价格数据
     *
     * @param bccId 预测币
     * @return 预测价格数据
     */
    public Set<TradePointVO> getTradePoint(String bccId, BccPageVo bccPageVo) {
        Set<TradePointVO> pointVoSet = Sets.newLinkedHashSet();
        Date dataFrom = DateUtils.addDays(new Date(), -3);
        List<BccTradingPoint> pointList = bccTradingPointDao.findByBccIdAndDsBetween(bccId, dataFrom, new Date());
        BccBasicInfo basicInfo = bccBasicInfoDao.findByBccId(bccId);
        for (BccTradingPoint point : pointList) {

            TradePointVO tradePointVO = new TradePointVO();
            tradePointVO.setPrice(BigDecimal.valueOf(point.getCurrentRate()).setScale(4, BigDecimal.ROUND_HALF_UP).toString());
            tradePointVO.setTradePair(basicInfo.getSymbol() + "/CNY");
            tradePointVO.setTradeTime(DateFormatUtils.format(point.getDs(), "yyyy-MM-dd HH:mm:ss"));
            tradePointVO.setTradeType(Objects.equals(point.getReachUp(), 1) ? 1 : 0);
            BigDecimal earnings = BigDecimal.valueOf(point.getExpectedReturn() * 100).setScale(2, BigDecimal.ROUND_HALF_UP);
            if (earnings.compareTo(BigDecimal.ZERO) < 0) {
                //预计收益为负 不进行推荐
                continue;
            }
            tradePointVO.setEarnings(earnings + "%");

            pointVoSet.add(tradePointVO);

            //推荐卖点价格
            if (Objects.equals(point.getReachUp(), 1) && StringUtils.isEmpty(bccPageVo.getRecommend().getSellPointPrice())) {
                //建议卖
                bccPageVo.getRecommend().setSellPointPrice(
                        BigDecimal.valueOf(point.getCurrentRate()).setScale(4, BigDecimal.ROUND_HALF_UP).toString()
                );
            }
            //推荐买价格
            if (Objects.equals(point.getReachLow(), 1) && StringUtils.isEmpty(bccPageVo.getRecommend().getBuyPointPrice())) {
                //建议卖
                bccPageVo.getRecommend().setBuyPointPrice(
                        BigDecimal.valueOf(point.getCurrentRate()).setScale(4, BigDecimal.ROUND_HALF_UP).toString()
                );
            }
        }
        return pointVoSet;
    }
}